function Stream(string){
    this.string = string;
    this.index = 0;
    this.index0 = this.index;
    this.ch = string.charAt(this.index);
}
(function (p){
    p.end = function (){
        this.index = this.string.length;
        this.ch = '';
    };
    p.isEnd = function (){
        return !this.ch;
    };
    p.peek = function (m, n){
        return this.string.substr(this.index + (m | 0), n || 1);
    };
    p.next = function (){
        this.ch = this.string.charAt(++this.index);
    };
    p.nextN = function (n){
        this.ch = this.string.charAt((this.index += n));
    };
    p.backN = function (n){
        this.ch = this.string.charAt((this.index -= n));
    };
    p.until = function (pattern){
        return this._scan(pattern, true, false);
    };
    p.skipUntil = function (pattern){
        return this._scan(pattern, true, true);
    };
    p.skipTo = function (pattern){
        return this._scan(pattern, false, true);
    };
    p._scan = function (pattern, force, peek){
        var index;
        var match = '';
        if (typeof pattern === 'string') {
            index = this.string.indexOf(pattern, this.index);
            if (index !== -1) {
                match = pattern;
            }
        } else {
            pattern.lastIndex = this.index;
            match = pattern.exec(this.string);
            if (match) {
                index = match.index;
                match = match[0];
            }
        }
        if (match) {
            if (peek) {
                this.index = index;
                this.ch = match.charAt(0);
            } else {
                this.index = index + match.length;
                this.ch = this.string.charAt(this.index);
            }
            return match;
        }
        if (force) {
            this.end();
            return '';
        }
    };
    p.current = function (){
        return this.string.slice(this.index0, this.index);
    };
    p.advance = function (){
        this.index0 = this.index;
    };
})(Stream.prototype);
